#include <stdio.h>
#include <assert.h>
#include "execute.h"
#include "evaluate.h"
#include "backend.h"

void execute_list(struct execute_result *xr, int count, struct statement *table, struct access_table *at, int context)
{
	int i;

	for (i = 0; i < count; i++) {
		execute(xr, &table[i], at, context);
		if (xr->status == XC_CONTINUE || xr->status == XC_BREAK || xr->status == XC_RETURN || xr->status == XC_EVENTTRANS) {
			break;
		}
	}
}

void execute(struct execute_result *xr, struct statement *statement, struct access_table *at, int context)
{
	struct evaluate_result vr, vs;

	switch (statement->cate) {
	case EXPRESSION:
		evaluate(&vr, statement->spec.s_expression.expression, at, context);
		xr->status = XC_NORMAL;
		break;
	case IF:
		evaluate(&vr, statement->spec.s_if.condition, at, context | CONTEXT_BOOLEAN);
		if (vr.spec.boolean.boolean == true)
			execute_list(xr, statement->spec.s_if.statement_count, statement->spec.s_if.statement_table, at, context);
		break;
	case IF_ELSE:
		evaluate(&vr, statement->spec.s_if.condition, at, context | CONTEXT_BOOLEAN);
		if (vr.spec.boolean.boolean == true)
			execute_list(xr, statement->spec.s_if_else.truestatement_count, statement->spec.s_if_else.truestatement_table, at, context);
		else
			execute_list(xr, statement->spec.s_if_else.falsestatement_count, statement->spec.s_if_else.falsestatement_table, at, context);
		break;
	case WHILE:
		WHILE_BEGIN:
		evaluate(&vr, statement->spec.s_while.condition, at, context | CONTEXT_BOOLEAN);
		if (vr.spec.boolean.boolean == false) {
			break;
		}
		execute_list(xr, statement->spec.s_while.statement_count, statement->spec.s_while.statement_table, at, context | CONTEXT_LOOP);
		if (xr->status == XC_CONTINUE) {
			xr->status = XC_NORMAL;
		}
		if (xr->status == XC_BREAK) {
			xr->status = XC_NORMAL;
			break;
		}
		goto WHILE_BEGIN;
	case DO_WHILE:
		DO_WHILE_BEGIN:
		execute_list(xr, statement->spec.s_do_while.statement_count, statement->spec.s_do_while.statement_table, at, context | CONTEXT_LOOP);
		if (xr->status == XC_BREAK) {
			xr->status = XC_NORMAL;
			break;
		}
		evaluate(&vr, statement->spec.s_do_while.condition, at, context | CONTEXT_BOOLEAN);
		if (xr->status == XC_CONTINUE) {
			xr->status = XC_NORMAL;
		}
		if (vr.spec.boolean.boolean == false) {
			break;
		}
		goto DO_WHILE_BEGIN;
	case FOR:
		evaluate(&vr, statement->spec.s_for.initialization, at, context);
		FOR_BEGIN:
		evaluate(&vr, statement->spec.s_for.condition, at, context | CONTEXT_BOOLEAN);
		if (vr.spec.boolean.boolean == false) {
			break;
		}
		execute_list(xr, statement->spec.s_for.statement_count, statement->spec.s_for.statement_table, at, context | CONTEXT_LOOP);
		if (xr->status == XC_CONTINUE) {
			xr->status = XC_NORMAL;
		}
		if (xr->status == XC_BREAK) {
			xr->status = XC_NORMAL;
			break;
		}
		evaluate(&vr, statement->spec.s_for.iteration, at, context);
		goto FOR_BEGIN;
	case BREAK:
		assert((context & CONTEXT_LOOP) == CONTEXT_LOOP);
		xr->status = XC_BREAK;
		break;
	case CONTINUE:
		assert((context & CONTEXT_LOOP) == CONTEXT_LOOP);
		xr->status = XC_CONTINUE;
		break;
	case RETURN:
		if (statement->spec.s_return.expression != NULL)
			evaluate(&xr->spec.x_return.evaluation, statement->spec.s_return.expression, at, context);
		xr->status = XC_RETURN;
		break;
	case EVENTCALL:
		assert((context & CONTEXT_FUNCTION) != CONTEXT_FUNCTION);
		call_event(statement->spec.s_eventcall.eventindex, at, context);
		xr->status = XC_NORMAL;
		break;
	case EVENTTRANS:
		assert((context & CONTEXT_FUNCTION) != CONTEXT_FUNCTION);
		xr->status = XC_EVENTTRANS;
		xr->spec.x_trans.eventindex = statement->spec.s_eventtrans.eventindex;
		break;
	case INPUT:
		evaluate(&vr, statement->spec.s_input.screen, at, context | CONTEXT_INDEX);
		evaluate(&vs, statement->spec.s_input.destination, at, context | CONTEXT_LVALUE);
		execute_input(vr.spec.integer.integer, &vs);
		xr->status = XC_NORMAL;
		break;
	case OUTPUT:
		evaluate(&vr, statement->spec.s_output.screen, at, context | CONTEXT_INDEX);
		execute_output(vr.spec.integer.integer, statement->spec.s_output.string, at, context & CONTEXT_G_ENV);
		xr->status = XC_NORMAL;
		break;
	default:
		assert(false);
		break;
	}

	// clear evaluation result in vr
}

void execute_select_enum_basic(int eidx, int *n)
{
	int i;
	for (i = 0; i < enum_basic_table[eidx].count; i++) {
		printf("%d\t%s\t%s\n", i, enum_basic_table[eidx].elements[i].identifier, enum_basic_table[eidx].elements[i].desc);
	}
	do {
		scanf("%d", n);
	} while (*n < 0 || *n >= enum_basic_table[eidx].count);
}

void execute_select_enum_combo(int eidx, int *n)
{
	int i, sidx;

	for (i = 0; i < enum_combo_table[eidx].compo_count; i++) {
		sidx = enum_combo_table[eidx].components[i];
		execute_select_enum_basic(sidx, n+i);
	}
}

void execute_input(int screen, struct evaluate_result *dst)
{
	int n;

	assert((dst->context & CONTEXT_LVALUE) == CONTEXT_LVALUE);
	printf("Virtual Screen #%d (INPUT):\n", screen);

	switch (type_table[dst->type].cate) {
	case ENUM_COMBO:
		printf("Please select a set of values to form a combo:\n");
		execute_select_enum_combo(type_table[dst->type].spec.enum_combo.enumindex, dst->location);
		validate_enum_combo_copy_noalloc(dst->type, dst->spec.enum_combo.elemindice, dst->location);
		break;
	case ENUM_BASIC:
		printf("Please select a value by enter the number:\n");
		execute_select_enum_basic(type_table[dst->type].spec.enum_basic.enumindex, &n);
		dst->spec.enum_basic.elemindex = n;
		*(int *)dst->location = n;
		break;
	case INTEGER:
		printf("Please enter an integer value: ");
		scanf("%d", &n);
		dst->spec.integer.integer = n;
		*(int *)dst->location = n;
		break;
	case BOOLEAN:
		printf("Please enter a boolean value, 0 for False, other for True: ");
		scanf("%d", &n);
		dst->spec.boolean.boolean = n == 0 ? false : true;
		*(bool *)dst->location = dst->spec.boolean.boolean;
		break;
	default:
		break;
	}
	printf("\n");
}


void execute_output2(int screen, char *string ,struct access_table *at, int context)
{
	void *content;
	int type, index, i, *compo, compoindex, elemindex;

	access_variable_content(&content, &type, string, at, context);

	switch (type_table[type].cate) {
	case ENUM_COMBO:
		index = type_table[type].spec.enum_basic.enumindex;
		compo = content;

		printf("OUTPUT[VS#%d] - ", screen);
		for (i = 0; i < enum_combo_table[index].compo_count; i++) {
			compoindex = enum_combo_table[index].components[i];
			printf("%s\t", enum_basic_table[compoindex].elements[compo[i]].desc);
		}
		puts("");
		break;
	case ENUM_BASIC:
		index = type_table[type].spec.enum_basic.enumindex;
		elemindex = *(int *)content;
		printf("OUTPUT[VS#%d] - %s\n", screen, enum_basic_table[index].elements[elemindex].desc );
		break;
	case INTEGER:
		printf("OUTPUT[VS#%d] - %d\n", screen, *(int *)content);
		break;
	case BOOLEAN:
		printf("OUTPUT[VS#%d] - %s\n", screen, *(bool *)content ? "True" : "False");
		break;
	default:
		assert(false);
		break;
	}
}

void execute_output(int screen, char *string, struct access_table *at, int context)
{
	if (*string == '$') {
		execute_output2(screen, string + 1, at, context);
	} else {
		printf("OUTPUT[VS#%d] - %s\n", screen, string);
	}
}


